package com.qianji.petmall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qianji.common.exception.NoStockException;
import com.qianji.common.exception.RabbitEnum;
import com.qianji.common.to.SkuHasStockVo;
import com.qianji.common.to.mq.OrderTo;
import com.qianji.common.to.mq.StockDetailTo;
import com.qianji.common.to.mq.StockLockedTo;
import com.qianji.common.utils.PageUtils;
import com.qianji.common.utils.Query;
import com.qianji.common.utils.R;
import com.qianji.petmall.ware.dao.WareSkuDao;
import com.qianji.petmall.ware.entity.WareOrderTaskDetailEntity;
import com.qianji.petmall.ware.entity.WareOrderTaskEntity;
import com.qianji.petmall.ware.entity.WareSkuEntity;
import com.qianji.petmall.ware.feign.OrderFeignService;
import com.qianji.petmall.ware.service.WareOrderTaskDetailService;
import com.qianji.petmall.ware.service.WareOrderTaskService;
import com.qianji.petmall.ware.service.WareSkuService;
import com.qianji.petmall.ware.vo.OrderItemVo;
import com.qianji.petmall.ware.vo.OrderVo;
import com.qianji.petmall.ware.vo.SkuWareHasStockVo;
import com.qianji.petmall.ware.vo.WareSkuLockVo;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    private OrderFeignService orderFeignService;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private WareSkuDao wareSkuDao;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public PageUtils queryPageBySku(Map<String, Object> params) {
        String wareId = (String) params.get("wareId");
        QueryWrapper<WareSkuEntity> wareSkuEntityQueryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotEmpty(wareId)){
            wareSkuEntityQueryWrapper.eq("ware_id",wareId);
        }
        String skuId = (String) params.get("skuId");
        if (StringUtils.isNotEmpty(skuId)){
            wareSkuEntityQueryWrapper.eq("sku_id",skuId);
        }
        IPage<WareSkuEntity> page = this.page(new Query<WareSkuEntity>().getPage(params), wareSkuEntityQueryWrapper);
        return new PageUtils(page);
    }

    @Override
    public void updateWork(Long skuId, Integer skuNum, Long wareId) {
        QueryWrapper<WareSkuEntity> sku_id = new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId);
        List<WareSkuEntity> wareSkuEntities = baseMapper.selectList(sku_id);
        if (wareSkuEntities.size()==0){
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            this.save(wareSkuEntity);
        }else {
            baseMapper.UpdateFull(skuId,skuNum,wareId);
        }

    }


    //TODO 解锁库存
    private void updateStockLocked(StockLockedTo stockLockedTo) {
        SkuWareHasStockVo stockVo = new SkuWareHasStockVo();
        StockDetailTo taskDetail = stockLockedTo.getTaskDetail();
        stockVo.setWareId(taskDetail.getWareId());
        stockVo.setCount(taskDetail.getSkuNum());
        stockVo.setSkuId(taskDetail.getSkuId());
        //解锁库存
        wareSkuDao.updateLockerStock(stockVo);
        //修改库存状态
        WareOrderTaskDetailEntity detail = new WareOrderTaskDetailEntity();
        detail.setId(taskDetail.getId());
        //变为已解锁
        detail.setLockStatus(2);
        wareOrderTaskDetailService.updateById(detail);
    }

    //订单关闭，解锁库存
    //防止订单服务卡顿导致，库存没解锁
    @Transactional
    @Override
    public void releaseLock(OrderTo to) {
        //首先查询一下数据库的最新状态，防止重复解锁库存
        String orderSn = to.getOrderSn();
        List<WareOrderTaskDetailEntity> wareOrderTask = wareOrderTaskDetailService.getOrderStaticByOrderSn(orderSn);
        for (WareOrderTaskDetailEntity detail : wareOrderTask) {
            StockLockedTo stockLockedTo = new StockLockedTo();
            StockDetailTo stockDetailTo = new StockDetailTo();
            BeanUtils.copyProperties(detail,stockDetailTo);
            stockLockedTo.setTaskDetail(stockDetailTo);
            if (to.getStatus()==4){
                updateStockLocked(stockLockedTo);
            }

        }
    }

    /**
     * 解锁库存业务
     */
    @Override
    public void releaseLock(StockLockedTo to) {
            StockDetailTo taskDetail = to.getTaskDetail();
            //解锁
            //1、需要查询数据库工作单的详情
            WareOrderTaskDetailEntity byId = wareOrderTaskDetailService.getById(taskDetail.getId());
            /**
             * 如果有，说明是其它的业务失败了导致回滚了数据，这个时候需要我们去解锁库存
             */
            if (byId!=null){
                //说明工作单有信息
                //还需要去查询订单表的状态，和订单的数据，要是没有订单也得解锁库存，要是订单的状态是取消的也需要解锁库存
                WareOrderTaskEntity taskId = wareOrderTaskService.getById(byId.getTaskId());
                //TODO 远程调用获取订单消息
                R r = orderFeignService.getOrderItemStatus(taskId.getOrderSn());
                if (r.getCode()==0){
                    //返回成功
                    OrderVo data = r.getData(new TypeReference<OrderVo>() {});
                    if (data==null || data.getStatus() == 4){
                        //说明订单已经被取消了，可以解锁库存,订单为空说明订单服务回滚了没有数据也需要解锁库存
                        if (byId.getLockStatus() == 1){
                            updateStockLocked(to);
                            System.out.println("解锁成功");
                        }
                    }
                }else {
                    //说明没有查询到订单信息，订单失败了回滚了数据
                    //TODO basicReject：消息拒绝以后重新放入到队列里面，让别人继续消费解锁
                    throw new RuntimeException("远程调用服务失败");
                }
            }else {
                /**
                 * 如果没有，说明是库存系统导致的业务失败，会自动回滚库存信息，不用去解锁库存
                 */
            }
    }

    /**
     * 为某个订单锁定库存
     * (rollbackFor = NoStockException.class)
     * 默认只要是运行时异常都会回滚
     * @param vo
     * @return
     */
    @Transactional
    @Override
    public boolean orderLock(WareSkuLockVo vo) {
        //保存库存工资单的详情
        WareOrderTaskEntity taskDetail = new WareOrderTaskEntity();
        taskDetail.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(taskDetail);


        List<OrderItemVo> locks = vo.getLocks();
        List<Long> skuId = vo.getLocks().stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
        //查询所有的库存信息
        List<WareSkuEntity> wareSkuEntities = this.baseMapper.selectList(new QueryWrapper<WareSkuEntity>().lambda().in(CollectionUtils.isEmpty(skuId), WareSkuEntity::getSkuId, skuId));
        Map<Long, Long> sku = wareSkuEntities.stream().filter(a -> {
            return a.getStock() - a.getStockLocked() > 0;
        }).collect(Collectors.toMap(WareSkuEntity::getSkuId, WareSkuEntity::getWareId));


        //1、查询每个商品都在哪个仓库
        List<SkuWareHasStockVo> collect = locks.stream().map(item -> {
            SkuWareHasStockVo stock = new SkuWareHasStockVo();
            stock.setSkuId(item.getSkuId());
            stock.setCount(item.getCount());
            stock.setWareId(sku.get(item.getSkuId()));
            //查询商品在那里有库存
            return stock;
        }).collect(Collectors.toList());
        //锁定库存

        for (SkuWareHasStockVo stockVo : collect) {
            boolean skuStocked = false;
            Long wareId = stockVo.getWareId();
            if (wareId==null){
                throw new NoStockException(stockVo.getSkuId());
            }
           int count = this.baseMapper.doLockStock(stockVo);
            //锁定成功：把锁了几件的库存发送给mq
            //锁定失败：前面保存的工作单信息回滚了，
           if (count == 1){
                skuStocked = true;
                //TODO 告诉MQ锁库存成功
               WareOrderTaskDetailEntity detail = new WareOrderTaskDetailEntity(null,stockVo.getSkuId(),"",
                                                                               stockVo.getCount(),
                                                                               taskDetail.getId(),
                                                                               stockVo.getWareId(),
                                                                               1);
               wareOrderTaskDetailService.save(detail);
               //给MQ发送的信息封装
               StockLockedTo stockLockedTo = new StockLockedTo();
               stockLockedTo.setId(taskDetail.getId());
               StockDetailTo stockDetailTo = new StockDetailTo();
               BeanUtils.copyProperties(detail,stockDetailTo);
               stockLockedTo.setTaskDetail(stockDetailTo);
               //给MQ发送工作单信息
               rabbitTemplate.convertAndSend(RabbitEnum.EXCHANGE_RABBIT.getMq(),
                                               RabbitEnum.STOCK_LOCKED.getMq(),
                                               stockLockedTo);
           }else {
               //锁定库存失败
               throw new NoStockException(stockVo.getSkuId());
           }
            if (skuStocked==false){
                //当前商品所有仓库都没有锁住
                throw new NoStockException(stockVo.getSkuId());
            }
        }
        //肯定都锁定成功了
        return true;
    }

    @Override
    public List<SkuHasStockVo> hasSkuStock(List<Long> skuId) {
        List<SkuHasStockVo> collect = skuId.stream().map(item -> {
            SkuHasStockVo stockVo = new SkuHasStockVo();
            Long count = baseMapper.selectSkuStock(item);
            stockVo.setSkuId(item);
            stockVo.setHasStock(count==null?false : count > 0);
            return stockVo;
        }).collect(Collectors.toList());
        return collect;
    }


}